/*
 * AnalisadorLexico.java
 *
 * Classe representando um autômato para analisador léxico.
 *
 * Última modificação: 23/06/2010
 *
 * Autores:
 *    Felipe Lacet
 *    Gutenberg Neto
 *    Jader James
 *    Marcello Galdino
 *    Ronei Oliveira
 */
package lexico;

import ConsultaURL.URLCommunication;
import persistencia.DatabaseCommunication;
import corretorgramatical.Palavra;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AnalisadorLexico {

    private URLCommunication urlComunic;
    private DatabaseCommunication dbComunic;
    // tabela gerada pelo autômato
    private ArrayList<Palavra> tabela;
    private Estado estadoInicial;
    private Estado estadoAtual;
    private String palavraAtual;
    private String alfabeto;
    private int linhaPalavraAtual;
    private boolean caseSensitive;
    private String palavraErrada;
    private boolean retornoRemove;
    private int ultimaPontuacao;

    public AnalisadorLexico(URLCommunication urlComunic, DatabaseCommunication dbComunic,
            Estado estadoInicial, String alfabeto, boolean caseSensitive) {

        this.urlComunic = urlComunic;
        this.dbComunic = dbComunic;

        ultimaPontuacao = -1;

        if (estadoInicial == null) {
            System.err.println("Automato deve ser iniciado com estado valido. Abortando execucao");
            System.exit(1);
        }
        palavraAtual = "";
        palavraErrada = "";
        retornoRemove = false;
        this.alfabeto = alfabeto;
        System.out.println("Automato iniciado com alfabeto: \"" + alfabeto + "\"");

        linhaPalavraAtual = 0;
        this.estadoInicial = this.estadoAtual = estadoInicial;

        tabela = new ArrayList<Palavra>();

        this.caseSensitive = caseSensitive;
    }

    public int consume(char simbolo, int linha) {
        boolean desconhecido = false;
        String aux = "";
        aux += simbolo;
        if (!caseSensitive) {
            aux = aux.toLowerCase();
        }

        // caso o símbolo recebido não faça parte do alfabeto, indica
        // o erro e faz o processamento da palavra encontrada até a chegada
        // do símbolo, caso haja alguma
        if (alfabeto.indexOf(aux) == -1) {
            if (getPalavraAtual().length() > 0) {
                if (estadoAtual.ehFinal) {
                    if (estadoAtual.getNome().equals("IDENTIFICADOR")) {
                        String classe = null;

                        /*                        // ESSA PARTE SERA SUBSTITUIDA PELO BANCO DE DADOS
                        for (int i = 0; i < tabela.size(); i++) {
                        if (tabela.get(i).token.equalsIgnoreCase(getPalavraAtual())) {
                        classe = tabela.get(i).tipo;
                        break;
                        }
                        }
                         */
//                        if (getPalavraAtual().equals(",")) {
//                            classe = "pontuacao_intermediaria";
//                        } else if (getPalavraAtual().equals(".") || getPalavraAtual().equals("!") || getPalavraAtual().equals("?") || getPalavraAtual().equals(";")) {
//                            classe = "pontuacao";
//                        }

                        if (dbComunic.existe(getPalavraAtual()) && classe == null) {
                            classe = dbComunic.getClasseAtual();
                            tabela.add(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));
                            // VERIFICA O BD
                        } else {

                            try {
                                // TODO code application logic here
                                classe = URLCommunication.ConsultaPalavra(getPalavraAtual());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }

                            if (classe.length() == 0) {
                                tabela.add(new Palavra(getPalavraAtual(), "DESCONHECIDO", linhaPalavraAtual));
                                palavraErrada = getPalavraAtual();
                                desconhecido = true;
                            } else {
                                tabela.add(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));
                                // ADICIONAR NO BD
                                dbComunic.adicionarPalavra(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));

                            }

                        }

                    } else {
                        tabela.add(new Palavra(getPalavraAtual(), estadoAtual.getNome(), linhaPalavraAtual));
                        if (estadoAtual.getNome().equals("pontuacao"))
                            ultimaPontuacao = tabela.size() - 1;
                    }
                } else {
                    // caso o estado nao final seja logo em token de apenas
                    // não há como encontrar estados anteriores finais
                    if (getPalavraAtual().length() > 1) {
                        processaTokenEstadoNaoFinal(linha);
                    } else {
                        return -2;
                    }
                }
            }
            estadoAtual = estadoInicial;
            palavraAtual = "";
            linhaPalavraAtual = 0;

            return -1;
        }

        if (estadoAtual.getProximoEstado(simbolo) != null) {
            estadoAtual = estadoAtual.getProximoEstado(simbolo);
            palavraAtual += aux;
            linhaPalavraAtual = linha;
        } else {
            if (getPalavraAtual().length() > 0) {
                if (estadoAtual.ehFinal) {
                    if (estadoAtual.getNome().equals("IDENTIFICADOR")) {
                        String classe = null;

                        /*                        // ESSA PARTE SERA SUBSTITUIDA PELO BANCO DE DADOS
                        for (int i = 0; i < tabela.size(); i++) {
                        if (tabela.get(i).token.equalsIgnoreCase(getPalavraAtual())) {
                        classe = tabela.get(i).tipo;
                        break;
                        }
                        }
                         */
//                        if (getPalavraAtual().equals(",")) {
//                            classe = "pontuacao_intermediaria";
//                        } else if (getPalavraAtual().equals(".") || getPalavraAtual().equals("!") || getPalavraAtual().equals("?") || getPalavraAtual().equals(";")) {
//                            classe = "pontuacao";
//                        }
                        if (dbComunic.existe(getPalavraAtual()) && classe == null) {
                            classe = dbComunic.getClasseAtual();
                            tabela.add(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));
                            // VERIFICA O BD
                        } else {

                            try {
                                // TODO code application logic here
                                classe = URLCommunication.ConsultaPalavra(getPalavraAtual());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }

                            if (classe.length() == 0) {
                                tabela.add(new Palavra(getPalavraAtual(), "DESCONHECIDO", linhaPalavraAtual));
                                palavraErrada = getPalavraAtual();
                                desconhecido = true;
                            } else {
                                tabela.add(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));
                                // ADICIONAR NO BD
                                dbComunic.adicionarPalavra(new Palavra(getPalavraAtual(), classe, linhaPalavraAtual));
                            }

                        }

                    } else {
                        tabela.add(new Palavra(getPalavraAtual(), estadoAtual.getNome(), linhaPalavraAtual));
                        if (estadoAtual.getNome().equals("pontuacao"))
                            ultimaPontuacao = tabela.size() - 1;
                        //System.out.println(palavraAtual + " / " + estadoAtual.getNome() + " / " + linhaPalavraAtual);
                    }
                } else {
                    // caso o estado nao final seja logo em token de apenas
                    // não há como encontrar estados anteriores finais
                    if (getPalavraAtual().length() > 1) {
                        processaTokenEstadoNaoFinal(linha);
                    } else {
                        return -2;
                    }
                }
            }
            estadoAtual = estadoInicial;
            palavraAtual = "";
            linhaPalavraAtual = 0;

            if (estadoAtual.getProximoEstado(simbolo) != null) {
                estadoAtual = estadoAtual.getProximoEstado(simbolo);
                palavraAtual += aux;
                linhaPalavraAtual = linha;
            }
        }

        if (desconhecido) {
            return -3;
        }

        return 0;
    }

    /*
     * Método que trata casos de tokens que param em estado não final, volta
     * os estados até encontrar estados que possam ser colocados na tabela.
     * Entradas: linha - linha do token
     * Saída: nada
     */
    public void processaTokenEstadoNaoFinal(int linha) {
        String aux = getPalavraAtual();
        if (aux.length() > 1) {
            estadoAtual = estadoInicial;
            String aux2 = aux.substring(0, aux.length() - 1);
            palavraAtual = "";
            for (int i = 0; i < aux2.length(); i++) {
                consume(aux2.charAt(i), linha);
            }
            consume(' ', linha);
        }

        estadoAtual = estadoInicial;
        palavraAtual = "";
        consume(aux.charAt(aux.length() - 1), linha);
        consume(' ', linha);
    }

    /*
     * Imprime a tabela gerada pelo autômato
     * Entradas: nada
     * Saída: nada
     */
    public void imprimeTabela() {
        System.out.println("*** TABELA DE TOKENS ***");
        System.out.println("------------------------");
        for (int i = 0; i < tabela.size(); i++) {
            System.out.println(tabela.get(i));
        }
    }

    public ArrayList<Palavra> getTabela() {
        return tabela;
    }

    /*
     * Método getter para a palavra atual
     * Entradas: nada
     * Saída: retorna a palavra atual no autômato
     */
    public String getPalavraAtual() {
        return palavraAtual;
    }

    // retorna true se tentar apagar mais de uma vez sem q haja palavra atual
    public boolean removeUmCaractere() {
        if (palavraAtual.length() <= 0) {
            if (retornoRemove) {
                setRetornoRemove(false);
                return true;
            }
            setRetornoRemove(true);

            return false;
        }

        setRetornoRemove(false);
        palavraAtual = palavraAtual.substring(0, palavraAtual.length() - 1);
        return false;
    }

    public void resetaLexico() {
        palavraAtual = "";
        palavraErrada = "";
        ultimaPontuacao = -1;

        linhaPalavraAtual = 0;

        this.estadoInicial = this.estadoAtual = estadoInicial;

        tabela.clear();
    }

    public ArrayList<Palavra> retornaUltimaExpressao (int pontuacaoAnterior) {
        ArrayList<Palavra> expressao = new ArrayList<Palavra>();
        Palavra aux;
        for (int i = pontuacaoAnterior + 1; i <= ultimaPontuacao; i++) {
            if (i >= tabela.size()) return null;
            aux = tabela.get(i);
            expressao.add(new Palavra(aux.token, aux.tipo, aux.linha));
        }

        return expressao;
    }

    private boolean palavraExistente(String palavra) {

        //busca a palavra no banco de dados
        if (dbComunic.existe(palavra)) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * @return the palavraErrada
     */
    public String getPalavraErrada() {
        return palavraErrada;
    }

    /**
     * @param retornoRemove the retornoRemove to set
     */
    public void setRetornoRemove(boolean retornoRemove) {
        this.retornoRemove = retornoRemove;
    }

    /**
     * @return the ultimaPontuacao
     */
    public int getUltimaPontuacao() {
        return ultimaPontuacao;
    }
}
